/*
 Copyright (C) 2020 MaxED

 This file is part of TrenchBroom.

 TrenchBroom is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 TrenchBroom is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with TrenchBroom. If not, see <http://www.gnu.org/licenses/>.
 */

#include "ColorModel.h"

#include <QColorDialog>

#include "PreferenceManager.h"
#include "Preferences.h"
#include "QtUtils.h"
#include "io/PathQt.h"

#include "kd/contracts.h"
#include "kd/path_utils.h"


namespace tb::ui
{
ColorModel::ColorModel(QObject* parent)
  : QAbstractTableModel{parent}
{
  initialize();
}

void ColorModel::initialize()
{
  for (auto* pref : Preferences::staticPreferences())
  {
    auto* colorPref = dynamic_cast<Preference<Color>*>(pref);
    if (
      colorPref != nullptr
      && colorPref->persistencePolicy() != PreferencePersistencePolicy::ReadOnly)
    {
      m_colors.emplace_back(colorPref);
    }
  }

  m_colorsCount = static_cast<int>(m_colors.size());
}

// Reset to default values
void ColorModel::reset()
{
  auto& prefs = PreferenceManager::instance();
  for (auto* pref : m_colors)
  {
    prefs.resetToDefault(*pref);
  }

  emit dataChanged(createIndex(0, 0), createIndex(m_colorsCount - 1, 2));
}

int ColorModel::rowCount(const QModelIndex& /* parent */) const
{
  return m_colorsCount;
}

int ColorModel::columnCount(const QModelIndex& /* parent */) const
{
  return 3; // Color, Context, Description
}

QVariant ColorModel::headerData(
  const int section, const Qt::Orientation orientation, const int role) const
{
  if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
  {
    switch (section)
    {
    case 0:
      return QString{tr("Color")};
    case 1:
      return QString{tr("Context")};
    case 2:
      return QString{tr("Description")};
      switchDefault();
    }
  }

  return {};
}

QVariant ColorModel::data(const QModelIndex& index, const int role) const
{
  if (!checkIndex(index))
  {
    return {};
  }

  if (role == Qt::DisplayRole)
  {
    const auto* colorPreference = getColorPreference(index.row());

    switch (index.column())
    {
    case 0:
      return {}; // Leave first cell empty
    case 1:
      return io::pathAsQString(kdl::path_front(colorPreference->path()));
    case 2:
      return io::pathAsGenericQString(kdl::path_pop_front(colorPreference->path()));
      switchDefault();
    }
  }

  // Colorize the first cell background with the associated preference color
  if (role == Qt::BackgroundRole && index.column() == 0)
  {
    auto* colorPreference = getColorPreference(index.row());
    auto color = toQColor(pref(*colorPreference));
    color.setAlpha(255); // Ignore alpha

    return QBrush{color};
  }

  return {};
}

bool ColorModel::setData(
  const QModelIndex& index, const QVariant& value, const int /*role*/)
{
  if (!checkIndex(index) || index.column() != 0)
  {
    return false;
  }

  auto* colorPreference = getColorPreference(index.row());
  const auto color = toQColor(pref(*colorPreference));

  auto newColor = value.value<QColor>();
  newColor.setAlpha(color.alpha()); // Keep initial alpha...

  auto& prefs = PreferenceManager::instance();
  prefs.set(*colorPreference, fromQColor(newColor));

  emit dataChanged(index, index);
  return true;
}

Qt::ItemFlags ColorModel::flags(const QModelIndex& index) const
{
  if (checkIndex(index) && index.column() == 0)
  {
    return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
  }

  return Qt::ItemIsEnabled;
}

void ColorModel::pickColor(const QModelIndex& mi)
{
  if (checkIndex(mi))
  {
    // Get current color
    auto* colorPreference = getColorPreference(mi.row());
    auto color = toQColor(pref(*colorPreference));

    // Show dialog
    auto newColor = QColorDialog::getColor(
      color, nullptr, "Select new color", QColorDialog::DontUseNativeDialog);

    // Apply color (QColorDialog::getColor() returns an invalid color if the user cancels
    // the dialog)
    if (newColor.isValid())
    {
      // pickColor() can be called for column 1 or 2 if the user double-clicks those
      // columns, but we always edit column 0 (where the color is displayed)
      setData(index(mi.row(), 0), newColor, Qt::EditRole);
    }
  }
}

Preference<Color>* ColorModel::getColorPreference(const int index) const
{
  contract_pre(index < m_colorsCount);
  return m_colors[static_cast<size_t>(index)];
}

bool ColorModel::checkIndex(const QModelIndex& index) const
{
  return index.isValid() && index.column() < 3 && index.row() < m_colorsCount;
}

} // namespace tb::ui
